最近把 Card.IO 改造成一个 aar 依赖提供给公司的项目使用。这个 aar 中使用了几个 so 库。只不过没有提供 armeabi 类型的 so 库,而只有 armeabi-v7a 和 armeabi-v8a 之类的 so 库。所以初次提供给公司项目使用时,出现了找不到 so 的错误,也就是 UnsatisfiedLinkError 错误。也就是下面这样的提示:

后面我想尽办法提供了 armeabi 类型的 so 库,这个问题就算告一段落了,然后这还不是结束。今天,测试又发了一段崩溃日志,也就是下面这样的日志:

因为之前出现过找不到 so 库的问题,所以乍一看上面的日志,我们都以为又是找不到 so 库 (为我们的菜表示羞愧 🤩)。然后经过上网查找,并和 native 的代码对比,我才意识到,上面的日志其实是 so 库是找到了的,只不过是加载 so 库时,调用 JNI_onLoad 这个 native 方法时出错了。此外,向测试要了 debug 包,发现没有问题,所以初步断定是混淆的问题,然后就是排查了。

我把测试提供给我的 release 包反编译,然后和 JNI_onLoad 方法中的 native 代码对比,终于找到原因了:release 包中,我提供的库的某个方法被混淆掉了,但是这个方法是需要被 JNI_onLoad 方法调用的。所以问题就是:一个需要被 so 库调用的 Java 方法被混淆了!其实,很多时候我们的项目引入一些著名的第三方包 (比如 Knife 之类的) 时,都会在我们的项目的 Proguard 配置文件中声明不要混淆这些三方包的代码,为的就是避免上面这个问题。说一句显得有点废话的话:第三方包发布混淆包时,他们的 Proguard 配置文件只能在他们的 aar 包构建时生效,而在他们的 aar 构建到我们的项目中时,他们的 Proguard 早已鞭长莫及不起作用了。

回到我遇到的这个问题,我们本地自测时,很少会进行混淆,本地的 release 默认都是未混淆的 (minifyEnabled 是 false),所以自测的时候根本没有注意到这个问题。这次这个 bug 的解决办法之一是,在最终使用我的 aar 的项目中,显示声明不要混淆我这个 aar 中的某些代码,这就和上一段说的声明不混淆第三方包的代码是一样的。But,还有一种更好的方法,那就是使用 com.android.support.support-annotations 支持库中的 @Keep 注解,此注解可以标记哪些方法或者类在最终构建 apk 时不会被混淆掉 (当然在前面的 aar 构建等过程自然也不会混淆)。

总结一下,当前项目的 Proguard 配置文件只能在当前项目构建输出产物时起作用,而无法确保输出的产物交给其他项目集成时不被混淆。若要确保后者,那就可以用 support-annotations 支持库中的注解了。